local Class = require('lua.lang.Class')
local LuaTable = require('lua.vm.LuaTable')
local BufferedInputStream = require('lua.io.BufferedInputStream')
local LuaError = require('lua.vm.LuaError')
local FileInputStream = require('lua.io.FileInputStream')

--- @class Globals : LuaTable
--- @field loader Loader
--- @field compiler Compiler
--- @field unDumper UnDumper
local Globals = Class {
	__extends = LuaTable
}

--- @param t table
--- @return Globals
function Globals:new(t)
	--- @type Globals
	local o = {}
	o.t = t or {}
	return setmetatable(o, Globals)
end

--- @param is InputStream
--- @param chunkName string
--- @return Prototype
function Globals:compilePrototype(is, chunkName)
	if self.compiler == nil then
		error('No compiler.')
	end
	return self.compiler:compile(is, chunkName)
end

--- @param is InputStream
--- @param chunkName string
--- @param mode string
--- @return Prototype
function Globals:loadPrototype(is, chunkName, mode)
	if mode:find('b') then
		local unDumper = self.unDumper
		if unDumper == nil then
			error('No unDumper.')
		end
		if not is:markSupported() then
			is = BufferedInputStream:new(is)
		end
		is:mark(4)
		local p = unDumper:unDump(is, chunkName)
		if p ~= nil then
			return p
		end
		is:reset()
	end
	if mode:find('t') then
		return self:compilePrototype(is, chunkName)
	end
	LuaError:new('attempt to load a text chunk (mode is \39' .. mode .. '39)'):throw()
end

--- @param fileName string
--- @return LuaValue
function Globals:loadfile(fileName)
	local is = FileInputStream:new(fileName)
	local f = self:load(is, '@' .. fileName, 'bt', self)
	if f:isClosure() then
		--- @type LuaClosure
		local c = f
		c.sourceFile = fileName
	end
	is:close()
	return f
end

--- @param is InputStream
--- @param chunkName string
--- @param mode string
--- @param environment LuaValue
--- @return LuaValue
function Globals:load(is, chunkName, mode, environment)
	local p = self:loadPrototype(is, chunkName, mode)
	return self.loader:load(p, chunkName, environment)
end

--- @param compiler Compiler
--- @return void
function Globals:installCompiler(compiler)
	self.compiler = compiler
end

--- @param loader Loader
--- @return void
function Globals:installLoader(loader)
	self.loader = loader
end

--- @param unDumper UnDumper
--- @return void
function Globals:installUnDumper(unDumper)
	self.unDumper = unDumper
end

return Globals